﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary.LinearEquationSolvers.Preconditioners;

namespace SmartMathLibrary.LinearEquationSolvers
{
    /// <summary>
    /// This class represents an abstract layer for all non stationary iterative methods.
    /// </summary>
    [Serializable]
    public abstract class AbstractNonStationaryIterativeMethod
    {
        /// <summary>
        /// A field, which describes a possible precision error during the calculation.
        /// </summary>
        protected bool precisionError;

        /// <summary>
        /// This field saves the needed iterations to solve the system of equation.
        /// </summary>
        protected int neededIterations;

        /// <summary>
        /// This field saves the relative error of the result.
        /// </summary>
        protected double relativeError;

        /// <summary>
        /// The coefficient matrix to solve.
        /// </summary>
        protected LinearEquationSystem equationSystem;

        /// <summary>
        /// The preconditioner for the matrix.
        /// </summary>
        private readonly IPreconditioner preconditioner;

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractNonStationaryIterativeMethod"/> class.
        /// </summary>
        /// <param name="coefficientMatrix">The coefficient matrix of the equation system to solve.</param>
        /// <param name="resultVector">The result vector of the equation system to solve.</param>
        /// <param name="preconditioner">The preconditioner which should be used for the iterative method.</param>
        protected AbstractNonStationaryIterativeMethod(Matrix coefficientMatrix, GeneralVector resultVector,
                                                       IPreconditioner preconditioner)
        {
            this.precisionError = false;
            this.preconditioner = preconditioner;
            this.equationSystem = new LinearEquationSystem(coefficientMatrix, resultVector);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractNonStationaryIterativeMethod"/> class.
        /// </summary>
        /// <param name="equationSystem">The equation system to solve.</param>
        /// <param name="preconditioner">The preconditioner which should be used for the iterative method.</param>
        protected AbstractNonStationaryIterativeMethod(LinearEquationSystem equationSystem,
                                                       IPreconditioner preconditioner)
        {
            this.precisionError = false;
            this.preconditioner = preconditioner;
            this.equationSystem = new LinearEquationSystem(equationSystem);
        }

        /// <summary>
        /// Gets a value indicating whether a precision error occurs during the calculation.
        /// </summary>
        /// <value>True if a precision error occurs during the calculation otherwise, false.</value>
        public bool PrecisionError
        {
            get { return precisionError; }
        }

        /// <summary>
        /// Gets or sets the coefficient matrix of the equation system to solve.
        /// </summary>
        /// <value>The coefficient matrix of the equation system to solve.</value>
        public Matrix CoefficientMatrix
        {
            get { return equationSystem.CoefficientMatrix; }
            set { equationSystem.CoefficientMatrix = value; }
        }

        /// <summary>
        /// Gets or sets the result vector of the equation system to solve.
        /// </summary>
        /// <value>The result vector of the equation system to solve.</value>
        public GeneralVector ResultVector
        {
            get { return equationSystem.ResultVector; }
            set { equationSystem.ResultVector = value; }
        }

        /// <summary>
        /// Gets the preconditioner which should be used for the iterative method.
        /// </summary>
        /// <value>The preconditioner which should be used for the iterative method.</value>
        public IPreconditioner Preconditioner
        {
            get { return preconditioner; }
        }

        /// <summary>
        /// Gets the needed iterations to solve the system of equation.
        /// </summary>
        /// <value>The needed iterations to solve the system of equation.</value>
        public int NeededIterations
        {
            get { return neededIterations; }
        }

        /// <summary>
        /// Gets the relative error of the result.
        /// </summary>
        /// <value>The relative error of the result.</value>
        public double RelativeError
        {
            get { return relativeError; }
        }

        /// <summary>
        /// Solves the linear system of equation.
        /// </summary>
        /// <param name="startValues">The start values for x.</param>
        /// <param name="iterations">The maximum numbers of iterations to solve the system of equation.</param>
        /// <param name="precision">The precision of the result.</param>
        /// <returns>The vector which solves the linear system of equation Ax = b.</returns>
        public abstract GeneralVector SolveLinearSystem(GeneralVector startValues, int iterations, double precision);
    }
}